package org.example.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * 签名工具类
 * @author shenguangyang
 */
public class SignHelper {
    private static final Logger log = LoggerFactory.getLogger(SignHelper.class);

    private static final String SECRET_KEY_OF_WXH = "e10adc3949ba59abbe56e057f20f883f";

    private static final String APPLICATION_JSON = "application/json";

    public static void main(String[] args) throws Exception {
        // 参数签名算法测试
        HashMap<String, Object> signMap = new HashMap<>(16);
        String ts = String.valueOf(getTimestamp());
        String token = "";
        String nonce = "";
        signMap.put("token","daca676bb80e46a5a36e6abe85b1a86d");
        signMap.put("nonce","123456");
        signMap.put("uid","2");
        signMap.put("ts",ts);
        String verifySign = getSign(signMap, ts , token , nonce , SECRET_KEY_OF_WXH);
        System.out.println("得到签名sign1:"+verifySign);

        System.out.println(ts);
    }

    /**
     * 校验签名是否合法
     * @param params 访问的全部参数
     * @param request 请求
     * @param sign 客户端传过来的签名
     * @param secretKeyOfWxh 加密使用的秘钥
     * @return
     */
    public static Boolean checkSign(Map<String,Object> params ,
                                    HttpServletRequest request ,
                                    String sign , String secretKeyOfWxh) throws Exception {
        boolean flag= false;
        String token = request.getHeader("token");
        String ts = request.getHeader("ts");
        // 随机数，前端生成随机数主要保证签名的多变性
        String nonce = request.getHeader("nonce");
        String verifySign = getSign(params, ts , token , nonce , secretKeyOfWxh);
        log.debug("现在的sign-->>" + sign);
        log.debug("验证的sign-->>" + verifySign);
        if(sign.equals(verifySign)){
            flag = true;
        }
        return flag;
    }

    /**
     * 得到签名
     * 签名格式 data(字典升序) + token + key(随机生成的秘钥) + ts(当前时间戳timestamp) + nonce(随机数)
     * @param params 参数集合不含secretkey
     * @param secret 验证接口的secretkey
     * @param token 令牌
     * @param nonce 随机字符串
     * @param ts 时间戳
     * @return
     */
    public static String getSign(Map<String, Object> params, String ts ,
                                 String token , String nonce , String secret) throws IOException {
        String sign="";
        StringBuilder sb = new StringBuilder();
        //step1：先对请求参数排序
        Set<String> keyset = params.keySet();
        TreeSet<String> sortSet = new TreeSet<String>(keyset);
        // step2：把参数的key value链接起来 secretkey放在最后面，得到要加密的字符串
        for (String key : sortSet) {
            String value = String.valueOf(params.get(key));
            sb.append(key).append(value);
        }
        sb.append(token).append(secret).append(ts).append(nonce);
        byte[] md5Digest;
        // 得到Md5加密得到sign
        md5Digest = getMd5Digest(sb.toString());
        sign = byte2hex(md5Digest);
        return sign;
    }

    public static long getTimestamp(){
        long timestampLong = System.currentTimeMillis();
        return timestampLong / 1000;
    }


    public static String utf8Encoding(String value, String sourceCharsetName) {
        try {
            return new String(value.getBytes(sourceCharsetName), StandardCharsets.UTF_8);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException(e);
        }
    }



    private static byte[] getMd5Digest(String data) throws IOException {
        byte[] bytes = null;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            bytes = md.digest(data.getBytes(StandardCharsets.UTF_8));
        } catch (GeneralSecurityException gse) {
            throw new IOException(gse);
        }
        return bytes;
    }

    private static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (byte aByte : bytes) {
            String hex = Integer.toHexString(aByte & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }

}
